'use client';
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';

import { TableMapping } from '@/grpc_generated/flow';
import { DBType } from '@/grpc_generated/peers';
import { Button } from '@/lib/Button';
import { Icon } from '@/lib/Icon';
import { ProgressCircle } from '@/lib/ProgressCircle';
import { CDCConfig, TableMapRow } from '../../../dto/MirrorsDTO';
import {
  IsClickHousePeer,
  IsEventhubsPeer,
  IsQueuePeer,
  fetchPublications,
} from '../handlers';
import {
  AdvancedSettingType,
  MirrorSetting,
  SOFT_DELETE_COLUMN_NAME,
} from '../helpers/common';
import CDCField from './fields';
import TablePicker from './tablemapping';

interface MirrorConfigProps {
  settings: MirrorSetting[];
  mirrorConfig: CDCConfig;
  setter: Dispatch<SetStateAction<CDCConfig>>;
  destinationType: DBType;
  sourceType: DBType;
  rows: TableMapRow[];
  setRows: Dispatch<SetStateAction<TableMapRow[]>>;
}

export default function CDCConfigForm({
  settings,
  mirrorConfig,
  setter,
  destinationType,
  sourceType,
  rows,
  setRows,
}: MirrorConfigProps) {
  const [publications, setPublications] = useState<string[]>();
  const [show, setShow] = useState(false);
  const [loading, setLoading] = useState(false);
  const [scriptingEnabled, setScriptingEnabled] = useState(false);
  const handleChange = (val: string | boolean, setting: MirrorSetting) => {
    let stateVal: string | boolean = val;
    setting.stateHandler(stateVal, setter);
  };

  const getScriptingEnabled = async () => {
    const response = await fetch('/api/mirror-types/validation/scripting');
    const data = await response.json();
    setScriptingEnabled(data);
  };

  const adjustedSettings = useMemo(
    () =>
      settings!.map((setting) => {
        if (sourceType.toString() === DBType[DBType.BIGQUERY]) {
          if (setting.label == 'Snapshot Staging Path') {
            setting.advanced = undefined;
            setting.required = true;
          }
        }

        return setting;
      }),
    [settings, sourceType]
  );

  const normalSettings = useMemo(
    () =>
      adjustedSettings!.filter(
        (setting) =>
          !(
            (IsQueuePeer(destinationType) &&
              setting.advanced === AdvancedSettingType.QUEUE) ||
            setting.advanced === AdvancedSettingType.ALL
          )
      ),
    [adjustedSettings, destinationType]
  );

  const advancedSettings = useMemo(() => {
    return adjustedSettings!
      .map((setting) => {
        if (
          IsQueuePeer(destinationType) &&
          setting.advanced === AdvancedSettingType.QUEUE
        ) {
          setting.stateHandler(600, setter);
          return { ...setting, default: 600 };
        }
        if (setting.advanced === AdvancedSettingType.ALL) {
          return setting;
        }
      })
      .filter((setting) => setting !== undefined);
  }, [adjustedSettings, destinationType, setter]);

  const isPostgresSource = () =>
    sourceType.toString() === DBType[DBType.POSTGRES];
  const isPostgresDestination = () =>
    destinationType.toString() === DBType[DBType.POSTGRES];
  const isBigQuerySource = () =>
    sourceType.toString() === DBType[DBType.BIGQUERY];
  const isBigQueryDestination = () =>
    destinationType.toString() === DBType[DBType.BIGQUERY];
  const isSnowflakeDestination = () =>
    destinationType.toString() === DBType[DBType.SNOWFLAKE];
  const isClickhouseDestination = () =>
    destinationType.toString() === DBType[DBType.CLICKHOUSE];

  const supportsStagingPath = () =>
    isBigQuerySource() || isBigQueryDestination() || isSnowflakeDestination();
  const supportsSoftDelete = () =>
    isPostgresDestination() ||
    isBigQueryDestination() ||
    isSnowflakeDestination();
  const supportsHardDelete = () => !isClickhouseDestination();

  const shouldHidePublicationName = (label: string) => {
    return label === 'publication name' && !isPostgresSource();
  };

  const shouldHideSnapshotSettings = (label: string) => {
    return label.includes('snapshot') && !mirrorConfig.doInitialSnapshot;
  };

  const shouldHideReplicationSlot = (label: string) => {
    return (
      label === 'replication slot name' &&
      (mirrorConfig.doInitialSnapshot || !isPostgresSource())
    );
  };

  const shouldHideStagingPath = (label: string) => {
    return label.includes('staging path') && !supportsStagingPath();
  };

  const shouldHideEventhubsSettings = (label: string) => {
    return (
      IsEventhubsPeer(destinationType) &&
      (label.includes('initial copy') ||
        label.includes('initial load') ||
        label.includes('snapshot'))
    );
  };

  const shouldHideTypeSystem = (label: string) => {
    const isPostgresToPostgres = isPostgresSource() && isPostgresDestination();
    return !isPostgresToPostgres && label.includes('type system');
  };

  const shouldHideColumnName = (label: string) => {
    return !isBigQueryDestination() && label.includes('column name');
  };

  const shouldHideSoftDelete = (label: string) => {
    return label.includes('soft delete') && !supportsSoftDelete();
  };

  const shoudlHideHardDelete = (label: string) => {
    return label.includes('soft delete') && !supportsHardDelete();
  };

  const shouldHideClickhouseScript = (label: string) => {
    return (
      !scriptingEnabled && label.includes('script') && isClickhouseDestination()
    );
  };

  const shouldHideBigQuerySourceIncompatibleFields = (label: string) => {
    if (!isBigQuerySource()) {
      return false;
    }

    return (
      label.startsWith('initial copy') || // only initial copy is supported. Values are opt-in by default for BigQuery source.
      label.includes('cdc') || // CDC is not supported
      label.includes('sync interval')
    ); // sync interval is not applicable
  };

  const paramDisplayCondition = (setting: MirrorSetting) => {
    const label = setting.label.toLowerCase();

    // List of conditions that would hide the setting
    const hideConditions = [
      shouldHidePublicationName(label),
      shouldHideSnapshotSettings(label),
      shouldHideReplicationSlot(label),
      shouldHideStagingPath(label),
      shouldHideEventhubsSettings(label),
      shouldHideTypeSystem(label),
      shouldHideColumnName(label),
      shouldHideSoftDelete(label),
      shouldHideClickhouseScript(label),
      shouldHideBigQuerySourceIncompatibleFields(label),
    ];

    // Show the setting if none of the hide conditions are true
    return !hideConditions.some((condition) => condition);
  };

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);

      try {
        const promises = [];
        if (sourceType.toString() === DBType[DBType.POSTGRES]) {
          promises.push(
            fetchPublications(mirrorConfig.sourceName ?? '').then((pubs) =>
              setPublications(pubs)
            )
          );
        }
        promises.push(getScriptingEnabled());

        await Promise.all(promises);
      } finally {
        setLoading(false);
      }
    };

    // Set soft delete configuration synchronously
    if (IsClickHousePeer(destinationType)) {
      setter((curr) => ({
        ...curr,
        softDeleteColName: '',
      }));
    } else {
      setter((curr) => ({
        ...curr,
        softDeleteColName: curr.softDeleteColName || SOFT_DELETE_COLUMN_NAME,
      }));
    }

    // Fetch data asynchronously
    fetchData();
  }, [
    sourceType,
    mirrorConfig.sourceName,
    mirrorConfig.initialSnapshotOnly,
    destinationType,
    setter,
  ]);

  if (loading) {
    return <ProgressCircle variant='determinate_progress_circle' />;
  }
  if (mirrorConfig.sourceName && mirrorConfig.destinationName)
    return (
      <>
        {normalSettings!.map(
          (setting, id) =>
            paramDisplayCondition(setting!) && (
              <CDCField
                key={id}
                handleChange={handleChange}
                setting={setting!}
                options={
                  setting?.label === 'Publication Name'
                    ? publications
                    : undefined
                }
              />
            )
        )}
        <Button
          className='IconButton'
          aria-label='collapse'
          onClick={() => {
            setShow((prev) => !prev);
          }}
          style={{
            width: '13em',
            height: '2.5em',
            marginTop: '2rem',
            marginBottom: '1rem',
          }}
        >
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <h3 style={{ marginRight: '10px' }}>Advanced Settings</h3>
            <Icon name={`keyboard_double_arrow_${show ? 'up' : 'down'}`} />
          </div>
        </Button>

        {show &&
          advancedSettings!.map(
            (setting) =>
              paramDisplayCondition(setting!) && (
                <CDCField
                  key={setting?.label}
                  handleChange={handleChange}
                  setting={setting!}
                />
              )
          )}

        <TablePicker
          sourcePeerName={mirrorConfig.sourceName}
          rows={rows}
          setRows={setRows}
          peerType={destinationType}
          alreadySelectedTablesMapping={new Map<string, TableMapping[]>()}
          initialLoadOnly={mirrorConfig.initialSnapshotOnly}
        />
      </>
    );
}
